﻿using ZyGame.Execute;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using System.Collections;

namespace ZyGame.Files
{
    sealed class VFSManager : Singleton<VFSManager>
    {
        /// <summary>
        /// vfs文件IO缓存时间
        /// </summary>
        private float cacheTime;

        /// <summary>
        /// 单个vfs的总大小
        /// </summary>
        private int vfsAllSize = 0;

        /// <summary>
        /// 单个vfs的数据块数量
        /// </summary>
        private int vfsFileCount = 0;

        /// <summary>
        /// 单个vfs文件，每个分块的大小
        /// </summary>
        private int vfsFileLenght = 0;

        /// <summary>
        /// 是否启用vfs
        /// </summary>
        private bool isUseVfs = false;

        /// <summary>
        /// 已加载的vfs的列表
        /// </summary>
        private List<VFStream> vfsList;

        /// <summary>
        /// 所有vfs数据块的数据
        /// </summary>
        private List<VFSOptions> optionsList;
#if UNITY_WEBGL
        [System.Runtime.InteropServices.DllImport("__Internal")]
        private static extern void OnSelectionFile();
#endif
        public VFSManager()
        {
            vfsList = new List<VFStream>();
            string cfg = AppConfig.GetLocalFilePath("vfs.ini");
            if (File.Exists(cfg))
            {
                optionsList = JsonConvert.DeserializeObject<List<VFSOptions>>(File.ReadAllText(cfg));
            }
            else
            {
                optionsList = new List<VFSOptions>();
            }

            vfsFileLenght = AppConfig.instance.vfsOptions.VFSSingleFileSize;
            vfsFileCount = AppConfig.instance.vfsOptions.VFSFileCount;
            cacheTime = AppConfig.instance.vfsOptions.IOCacheTime;
            vfsAllSize = vfsFileLenght * vfsFileCount;
            isUseVfs = AppConfig.instance.vfsOptions.isUseVFS;
            Client.Events.Register("APP_QUIT", Dispose);
            Client.Tools.AddUpdate(OnUpdate);
        }

        private void OnUpdate()
        {
            if (vfsList is null || vfsList.Count is 0)
            {
                return;
            }

            for (int i = vfsList.Count - 1; i >= 0; i--)
            {
                if (Time.realtimeSinceStartup - vfsList[i].lastAccessTime < cacheTime)
                {
                    continue;
                }

                vfsList[i].Dispose();
                vfsList.RemoveAt(i);
            }
        }

        private void SaveOptions()
        {
            string cfg = AppConfig.GetLocalFilePath("vfs.ini");
            File.WriteAllText(cfg, JsonConvert.SerializeObject(optionsList));
            Client.Console.WriteLine("Saveed VFS Config:" + cfg);
        }

        private void Dispose(object o)
        {
            for (int i = 0; i < vfsList.Count; i++)
            {
                vfsList[i].Dispose();
            }

            vfsList.Clear();
        }


        /// <summary>
        /// 打开文件选择窗口
        /// </summary>
        public void OpenFileWindow(string path)
        {
#if UNITY_WEBGL
            OnSelectionFile();
#endif
        }

        /// <summary>
        /// 是否存在文件
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool Exists(string fileName, uint version = 0, uint crc = 0)
        {
            if (isUseVfs is false)
            {
                return File.Exists(AppConfig.GetLocalFilePath(fileName));
            }

            return optionsList.Where(x => x.name == fileName).Count() > 0;
        }

        public bool Delete(string fileName)
        {
            IEnumerable<VFSOptions> options = optionsList.Where(x => x.name == fileName);
            foreach (var item in options)
            {
                item.sort = 0;
                item.lenght = 0;
                item.name = string.Empty;
            }

            return options.Count() > 0;
        }

        /// <summary>
        /// 读取文件数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public byte[] ReadData(string fileName, uint version = 0, uint crc = 0)
        {
            if (isUseVfs is false)
            {
                return File.ReadAllBytes(AppConfig.GetLocalFilePath(fileName));
            }

            VFSOptions[] options = optionsList.Where(x => x.name == fileName).ToArray();
            if (options is null || options.Length is 0)
            {
                throw new FileNotFoundException(fileName);
            }

            VFStream stream = vfsList.Find(x => x.name == options[0].vfsName);
            if (stream is null)
            {
                stream = new VFStream(options[0].vfsName);
                vfsList.Add(stream);
            }

            return stream.ReadData(options);
        }

        /// <summary>
        /// 异步读取数据
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public Runnable<byte[]> ReadDataAsync(string fileName, uint version = 0, uint crc = 0)
        {
            if (Client.Running.TryGetOrCreate(fileName, out Runnable<byte[]> runnable))
            {
                return runnable;
            }

            IEnumerator Running()
            {
                yield return new WaitForEndOfFrame();
                runnable.SetResult(ReadData(fileName, version, crc));
            }

            runnable.Execute(Running);
            return runnable;
        }

        private VFStream GetStream(int lenght)
        {
            lock (instance)
            {
                Dictionary<string, List<VFSOptions>> ops = new Dictionary<string, List<VFSOptions>>();
                for (int i = 0; i < optionsList.Count; i++)
                {
                    if (!ops.TryGetValue(optionsList[i].vfsName, out List<VFSOptions> list))
                    {
                        ops.Add(optionsList[i].vfsName, list = new List<VFSOptions>());
                    }

                    list.Add(optionsList[i]);
                }

                VFStream result = null;
                foreach (var item in ops.Values)
                {
                    int usege = item.Sum(x => x.lenght);
                    if (vfsAllSize - usege >= lenght)
                    {
                        result = vfsList.Find(x => x.name == item[0].vfsName);

                        break;
                    }
                }

                if (result is null)
                {
                    string newVfsName = $"{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.vfs";
                    vfsList.Add(result = new VFStream(newVfsName));
                    Client.Console.WriteLine("create new vfs name:" + newVfsName);
                    for (int i = 0; i < vfsFileCount; i++)
                    {
                        optionsList.Add(new VFSOptions
                        {
                            offset = i * vfsFileLenght,
                            name = string.Empty,
                            vfsName = result.name,
                            lenght = 0,
                            sort = 0
                        });
                    }

                    SaveOptions();
                }

                return result;
            }
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="bytes"></param>
        public void WriterData(string fileName, byte[] bytes, uint version = 0, uint crc = 0)
        {
            if (isUseVfs is false)
            {
                File.WriteAllBytes(AppConfig.GetLocalFilePath(fileName), bytes);
                return;
            }

            Delete(fileName);

            VFStream stream = GetStream(bytes.Length);
            int temp = bytes.Length / vfsFileLenght;
            temp += bytes.Length - temp * vfsFileLenght > 0 ? 1 : 0;
            IEnumerable<VFSOptions> options = optionsList.Where(x => x.lenght == 0 && x.vfsName == stream.name);
            if (options is null || options.Count() is 0 || options.Count() < temp)
            {
                throw new Exception(String.Format("{0} {1} {2} {3}", "vfs error", options is null, options.Count() is 0, options.Count() < temp));
            }

            VFSOptions[] s = new VFSOptions[temp];
            for (int i = 0; i < s.Length; i++)
            {
                s[i] = options.ElementAt(i);
            }

            stream.WriteData(fileName, bytes, s);
            SaveOptions();
        }

        /// <summary>
        /// 异步写入数据
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public Runnable WriteDataAsync(string fileName, byte[] bytes, uint version = 0, uint crc = 0)
        {
            if (Client.Running.TryGetOrCreate(fileName, out Runnable runnable))
            {
                return runnable;
            }

            IEnumerator Running()
            {
                yield return new WaitForEndOfFrame();
                WriterData(fileName, bytes, version, crc);
                runnable.SetResult(bytes);
            }

            runnable.Execute(Running);
            return runnable;
        }
    }
}